package com.dragon.common.spring.openapi.customizers.openapi;

import com.dragon.common.spring.openapi.SchemaHelper;
import com.dragon.common.spring.openapi.httppack.OpenApiConstants;
import com.google.gson.Gson;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.media.IntegerSchema;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.media.StringSchema;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springdoc.core.customizers.OpenApiCustomizer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Order(-100)
@Component
public class HttpResultOpenApiCustomizer implements OpenApiCustomizer {
    private static final Logger log = LoggerFactory.getLogger(HttpResultOpenApiCustomizer.class);

    @Autowired
    private Gson gson;

    @Override
    public void customise(OpenAPI openApi) {
        log.info("HttpResultOpenApiCustomizer~~");
        // 确保HttpResult Schema存在
        ensureHttpResultSchemaExists(openApi);

        // 遍历所有路径
        openApi.getPaths().forEach((path, pathItem) -> {
            // 处理所有HTTP方法
            pathItem.readOperationsMap().forEach((httpMethod, operation) -> {
                // 检查是否需要跳过包装（方法返回类型已经是HttpResult）
                if (shouldSkipWrapHttpResult(operation)) {
                    return;
                }
                // 3. 读取扩展标记决定是否包装
                if (!shouldWrapHttpResult(operation)) {
                    return;
                }

                // 包装响应
                wrapOperationResponsesWithHttpResult(openApi, operation);
            });
        });
    }

    /**
     * 检查是否需要包装HttpResult
     *
     * @param operation
     * @return
     */
    private boolean shouldWrapHttpResult(Operation operation) {
        Object flag = operation.getExtensions().get(OpenApiConstants.X_WRAP_WITH_HTTP_RESULT);
        return flag instanceof Boolean ? (Boolean) flag : false;
    }

    private void ensureHttpResultSchemaExists(OpenAPI openApi) {
        if (openApi.getComponents() == null) {
            openApi.setComponents(new Components());
        }

        SchemaHelper.addHttpResultSchema(openApi.getComponents());
    }

    /**
     * 检查是否需要跳过包装HttpResult
     *
     * @param operation
     * @return
     */
    private boolean shouldSkipWrapHttpResult(Operation operation) {
        // 如果有自定义扩展属性标记已经包装过，则跳过
        if (operation.getExtensions() != null &&
                operation.getExtensions().containsKey(OpenApiConstants.X_HTTP_RESULT_ALREADY_WRAPPED)) {
            return true;
        }

        // 检查响应结构是否已经是HttpResult格式
        return operation.getResponses().values().stream()
                .flatMap(response -> response.getContent().values().stream())
                .anyMatch(content -> {
                    Schema<?> schema = content.getSchema();
                    return schema != null && schema.get$ref() != null &&
                            schema.get$ref().contains("HttpResult");
                });
    }

    /**
     * 包裹HttpResult
     *
     * @param openApi
     * @param operation
     */
    private void wrapOperationResponsesWithHttpResult(OpenAPI openApi, Operation operation) {
        operation.getResponses().forEach((statusCode, response) -> {
            if (response.getContent() != null) {
                response.getContent().forEach((mediaType, content) -> {
                    Schema<?> originalSchema = content.getSchema();

                    // 跳过空Schema
                    if (originalSchema == null) {
                        return;
                    }

                    // 更新HttpResult的data属性
                    updateHttpResultDataSchema(openApi, originalSchema);
                });
            }
        });

        // 标记已经处理过
        operation.addExtension(OpenApiConstants.X_HTTP_RESULT_ALREADY_WRAPPED, true);
    }

    /**
     * 包裹HttpResult
     *
     * @param openApi
     * @param originalSchema
     */
    private void updateHttpResultDataSchema(OpenAPI openApi, Schema<?> originalSchema) {
        // 基本数据类型包裹ValueResult
        if (originalSchema.get$ref() == null) {
            // 就算是没有ValuePack注解，基本数据类型要被HttpResult包裹，也必须先被ValueResult包裹
            updateValueResultDataSchema(openApi, originalSchema);
        }
        // 再次判断，如果处理不成功，就不管了
        if (originalSchema.get$ref() == null) {
            System.out.println("没处理好");
            return;
        }
        // 原有引用
        String ref = originalSchema.get$ref();
        String[] split = ref.split("/");
        if (split.length != 4) {
            updateValueResultDataSchema(openApi, originalSchema);
            return;
        }

        String originalSchemaName = split[3];
        // 新的Schema名称
        String newSchemaName = "HttpResult" + originalSchemaName;
        // 检查是否已存在，不存在要创建
        Schema<?> httpResultSchema;
        if (openApi.getComponents().getSchemas().containsKey(newSchemaName)) {
            httpResultSchema = openApi.getComponents().getSchemas().get(newSchemaName);
        } else {
            httpResultSchema = new Schema<>()
                    .name(newSchemaName)
                    .type("object")
                    .addProperty("code", new IntegerSchema().example(0))
                    .addProperty("message", new StringSchema().example("success"))
                    .addProperty("data", new Schema<>().type("object"));
            // 包裹
            // 创建data属性的Schema
            Schema<?> dataSchema = new Schema<>();

            if (ref != null) {
                // 如果原始Schema是引用类型，直接使用
                dataSchema.set$ref(ref);
            } else {
                // 对于内联Schema，生成一个名称并注册
                String schemaName = "InlineData_" + System.currentTimeMillis();
                openApi.getComponents().addSchemas(schemaName, originalSchema);
                dataSchema.set$ref("#/components/schemas/" + schemaName);
            }

            // 更新HttpResult的data属性
            httpResultSchema.addProperty("data", dataSchema);

            // 在Schema列表中添加新生成的Schema
            String schemaName = "HttpResult" + dataSchema.get$ref().split("/")[3];
            openApi.getComponents().addSchemas(schemaName, httpResultSchema);
        }

        // 重新指向
        originalSchema.set$ref("#/components/schemas/" + newSchemaName);
    }

    /**
     * 包裹ValueResult
     * 借用ValueResultOpenApiCustomizer的部分代码
     *
     * @param openApi
     * @param originalSchema
     * @return
     */
    private void updateValueResultDataSchema(OpenAPI openApi, Schema<?> originalSchema) {
        // 原有的Schema名称
        String ref = originalSchema.get$ref();
        // 如果不为空，就是引用类型，不处理
        if (ref != null) {
            return;
        }
        // 包裹ValueResult
        String type = originalSchema.getType();
        String originalSchemaName = convertSchemaName(type);

        // 新的Schema名称
        String newSchemaName = "ValueResult" + originalSchemaName;
        // 检查是否已存在，不存在要创建
        Schema<?> valueResultSchema;
        if (openApi.getComponents().getSchemas().containsKey(newSchemaName)) {
            valueResultSchema = openApi.getComponents().getSchemas().get(newSchemaName);
        } else {
            valueResultSchema = new Schema<>()
                    .name(newSchemaName)
                    .type("object")
                    .addProperty("value", new Schema<>().type(type));

            // 在Schema列表中添加新生成的Schema
            openApi.getComponents().addSchemas(newSchemaName, valueResultSchema);
        }

        // 重新指向
        originalSchema.set$ref("#/components/schemas/" + newSchemaName);
    }

    /**
     * 转换Schema名称
     *
     * @param type
     * @return
     */
    private String convertSchemaName(String type) {
        if (StringUtils.isEmpty(type)) {
            return "";
        }
        // 首字母大写
        return StringUtils.capitalize(type);
    }

}